﻿using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net;

namespace Sgr.Oss.Services;

/// <summary>
/// S3兼容对象存储服务实现
/// </summary>
public class S3OssService : IOssService, IDisposable
{
    private readonly S3Options _options;
    private readonly ILogger<S3OssService> _logger;
    private readonly AmazonS3Client _s3Client;

    private readonly BucketCache _bucketCache;

    private volatile bool _disposed = false;

    /// <summary>
    /// 构造函数
    /// </summary>
    public S3OssService(IOptions<S3Options> options, ILogger<S3OssService> logger)
    {
        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _bucketCache = new BucketCache(TimeSpan.FromSeconds(_options.BucketCacheSeconds));

        _s3Client = CreateS3Client();

        // 验证连接
        if (_options.ValidateOnStartup)
        {
            ValidateConnection().GetAwaiter().GetResult();
        }
    }

    #region 辅助方法

    /// <summary>
    /// 创建S3客户端
    /// </summary>
    /// <returns></returns>
    protected virtual AmazonS3Client CreateS3Client()
    {
        // 创建S3客户端配置
        var s3Config = new AmazonS3Config
        {
            ServiceURL = _options.EndPoint,                                     // 服务地址
            ForcePathStyle = true,                                              // 对MinIO必须设置为true
            UseHttp = !_options.UseHttps,                                       // 是否使用HTTPS
            //RegionEndpoint = RegionEndpoint.GetBySystemName(_options.Region),   // 1.	MinIO 是一个独立的服务器，不是 AWS 的服务，所以不需要指定 AWS 的区域.    2.	设置了 RegionEndpoint 会导致 SDK 尝试验证 AWS 的凭证，这会引起访问失败
            Timeout = TimeSpan.FromSeconds(_options.TimeoutSeconds),
            MaxErrorRetry = _options.MaxRetryCount
        };

        // 创建凭证对象
        AWSCredentials credentials;
        if (!string.IsNullOrEmpty(_options.SessionToken))
        {
            credentials = new SessionAWSCredentials(
                _options.AccessKey,
                _options.SecretKey,
                _options.SessionToken);
        }
        else
        {
            credentials = new BasicAWSCredentials(
                _options.AccessKey,
                _options.SecretKey);
        }

        // 创建S3客户端
        return new AmazonS3Client(credentials, s3Config);
    }

    /// <summary>
    /// 验证与S3服务的连接
    /// </summary>
    protected virtual async Task ValidateConnection()
    {
        try
        {
            await _s3Client.ListBucketsAsync();
            _logger.LogInformation("S3存储服务连接成功: {Endpoint}", _options.EndPoint);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "S3存储服务连接失败: {Endpoint}, 错误: {Message}",
                _options.EndPoint, ex.Message);
            throw new InvalidOperationException($"无法连接到S3存储服务: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 处理存储桶操作错误
    /// </summary>
    /// <param name="bucketName"></param>
    /// <param name="ex"></param>
    protected virtual void HandleBucketOperationError(string bucketName, Exception ex)
    {
        if (ex is AmazonS3Exception s3Ex)
        {
            switch (s3Ex.ErrorCode)
            {
                // 确实是桶不存在，可以缓存结果
                case "NoSuchBucket":
                    _logger.LogWarning("存储桶不存在: {BucketName}", bucketName);
                    _bucketCache.Set(bucketName, false);
                    break;

                // 权限问题，移除缓存
                case "AccessDenied":
                case "InvalidAccessKeyId":
                case "SignatureDoesNotMatch":
                    _bucketCache.Remove(bucketName);
                    break;

                case "BucketNotEmpty":
                    _logger.LogError("存储桶不为空，无法删除: {BucketName}", bucketName);
                    break;
                // 其他未知情况，也移除缓存
                default:
                    _bucketCache.Remove(bucketName);
                    break;
            }
        }
        else
        {
            _bucketCache.Remove(bucketName);
        }
    }

    /// <summary>
    /// 处理存储对象操作错误
    /// </summary>
    /// <param name="bucketName">存储桶名称</param>
    /// <param name="objectName">对象名称</param>
    /// <param name="ex">异常</param>
    /// <returns>处理后的异常（如果需要继续抛出）</returns>
    protected virtual Exception HandleObjectOperationError(string bucketName, string objectName, Exception ex)
    {
        if (ex is AmazonS3Exception s3Ex)
        {
            switch (s3Ex.ErrorCode)
            {
                // 存储桶不存在
                case "NoSuchBucket":
                    _logger.LogWarning("存储桶不存在: Bucket={Bucket}", bucketName);
                    HandleBucketOperationError(bucketName, s3Ex);
                    return new DirectoryNotFoundException($"存储桶不存在: {bucketName}");

                // 对象不存在
                case "NoSuchKey":
                    _logger.LogDebug("对象不存在: Bucket={Bucket}, Object={Object}",
                        bucketName, objectName);
                    return new FileNotFoundException(
                        $"对象不存在: Bucket={bucketName}, Object={objectName}");

                // 访问被拒绝
                case "AccessDenied":
                    _logger.LogError("访问被拒绝: Bucket={Bucket}, Object={Object}",
                        bucketName, objectName);
                    HandleBucketOperationError(bucketName, s3Ex);
                    return new UnauthorizedAccessException(
                        $"访问被拒绝: Bucket={bucketName}, Object={objectName}");

                // 无效的访问密钥
                case "InvalidAccessKeyId":
                case "SignatureDoesNotMatch":
                    _logger.LogError("认证失败: Bucket={Bucket}, Object={Object}, ErrorCode={ErrorCode}",
                        bucketName, objectName, s3Ex.ErrorCode);
                    HandleBucketOperationError(bucketName, s3Ex);
                    return new UnauthorizedAccessException("S3认证失败，请检查访问密钥配置");

                // 对象已存在（针对某些特定操作）
                case "ObjectAlreadyExists":
                    _logger.LogWarning("对象已存在: Bucket={Bucket}, Object={Object}",
                        bucketName, objectName);
                    return new IOException($"对象已存在: Bucket={bucketName}, Object={objectName}");

                case "NotFound":
                    _logger.LogWarning("对象未找到: Bucket={Bucket}, Object={Object}",
                        bucketName, objectName);
                    return new FileNotFoundException($"对象未找到: Bucket={bucketName}, Object={objectName}");

                // 其他S3特定错误
                default:
                    _logger.LogError(s3Ex,
                        "S3操作失败: Bucket={Bucket}, Object={Object}, ErrorCode={ErrorCode}, Message={Message}",
                        bucketName, objectName, s3Ex.ErrorCode, s3Ex.Message);
                    // 如果是存储桶相关错误，更新存储桶缓存
                    if (s3Ex.ErrorCode.Contains("Bucket", StringComparison.OrdinalIgnoreCase))
                    {
                        HandleBucketOperationError(bucketName, s3Ex);
                    }
                    return ex;
            }
        }

        // 处理非S3异常
        _logger.LogError(ex, "对象操作失败: Bucket={Bucket}, Object={Object}",
            bucketName, objectName);
        return ex;
    }

    /// <summary>
    /// 检查存储桶是否为空
    /// </summary>
    protected virtual async Task<bool> IsBucketEmptyAsync(string bucketName, CancellationToken cancellationToken = default)
    {
        try
        {
            //在分析 S3 和 MinIO 兼容性时，我们需要区分标准 S3 API 和扩展功能。让我们检查这些参数的兼容性：
            //    1.FetchOwner 属性：
            //    •	这是 Amazon S3 ListObjectsV2 API 的标准参数
            //    •	在 AWS S3 文档中明确定义
            //    •	MinIO 支持这个参数但会忽略它，因为 MinIO 不实现详细的所有者信息
            //    2.RequestPayer 参数：
            //    •	这是 AWS S3 特有的功能，用于请求者付费场景
            //    •	MinIO 不支持此参数，会忽略它
            //    •	在 MinIO 环境中使用可能会导致不必要的开销

            //// 使用 HEAD 请求检查存储桶中的对象数量
            //var request = new ListObjectsV2Request
            //{
            //    BucketName = bucketName,
            //    MaxKeys = 1,
            //    // 设置为 true 只返回对象数量，不返回对象信息
            //    FetchOwner = false,
            //    // 不返回对象元数据信息
            //    RequestPayer = RequestPayer.Requester
            //};

            // 使用标准的 ListObjectsV2Request，只保留必要参数
            var request = new ListObjectsV2Request
            {
                BucketName = bucketName,
                MaxKeys = 1,     // 只需要知道是否有对象
                                 // 不设置其他可选参数，保持最大兼容性
            };

            var response = await _s3Client.ListObjectsV2Async(request, cancellationToken);

            // 直接使用 KeyCount 属性，而不是检查 S3Objects 集合
            return response.KeyCount == 0;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "检查存储桶是否为空失败: {BucketName}", bucketName);

            HandleBucketOperationError(bucketName, ex);

            throw;
        }
    }

    /// <summary>
    /// 将内部枚举值转换为AWS S3的权限设置
    /// </summary>
    private static S3CannedACL ConvertBucketAcl(BucketAcl acl)
    {
        return acl switch
        {
            BucketAcl.Private => S3CannedACL.Private,
            BucketAcl.PublicRead => S3CannedACL.PublicRead,
            BucketAcl.PublicReadWrite => S3CannedACL.PublicReadWrite,
            _ => S3CannedACL.Private
        };
    }

    /// <summary>
    /// 将内部枚举值转换为AWS S3的权限设置
    /// </summary>
    private static S3CannedACL ConvertObjectAcl(ObjectAcl acl)
    {
        return acl switch
        {
            ObjectAcl.Private => S3CannedACL.Private,
            ObjectAcl.PublicRead => S3CannedACL.PublicRead,
            ObjectAcl.PublicReadWrite => S3CannedACL.PublicReadWrite,
            _ => S3CannedACL.BucketOwnerFullControl  // 对于Inherited，使用存储桶拥有者完全控制
        };
    }

    /// <summary>
    /// 根据文件扩展名获取内容类型
    /// </summary>
    private static string? GetContentType(string filename)
    {
        var extension = Path.GetExtension(filename).ToLowerInvariant();
        return extension switch
        {
            ".jpg" => "image/jpeg",
            ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".bmp" => "image/bmp",
            ".webp" => "image/webp",
            ".svg" => "image/svg+xml",
            ".pdf" => "application/pdf",
            ".doc" => "application/msword",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".xls" => "application/vnd.ms-excel",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            ".ppt" => "application/vnd.ms-powerpoint",
            ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            ".txt" => "text/plain",
            ".html" => "text/html",
            ".htm" => "text/html",
            ".css" => "text/css",
            ".js" => "application/javascript",
            ".json" => "application/json",
            ".xml" => "application/xml",
            ".zip" => "application/zip",
            ".rar" => "application/x-rar-compressed",
            ".gz" => "application/gzip",
            ".7z" => "application/x-7z-compressed",
            ".tar" => "application/x-tar",
            ".mp3" => "audio/mpeg",
            ".mp4" => "video/mp4",
            ".avi" => "video/x-msvideo",
            ".mov" => "video/quicktime",
            ".mkv" => "video/x-matroska",
            ".webm" => "video/webm",
            _ => "application/octet-stream"
        };
    }

    #endregion 辅助方法

    #region 存储桶操作

    /// <summary>
    /// 检查存储桶是否存在
    /// </summary>
    public virtual async Task<bool> BucketAnyAsync(string bucketName, CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

        // 检查缓存
        if (_bucketCache.TryGet(bucketName, out var exists))
        {
            return exists;
        }

        try
        {
            // 刷新缓存
            var response = await _s3Client.ListBucketsAsync(cancellationToken);
            _bucketCache.UpdateAll(response.Buckets.Select(b => b.BucketName));

            return response.Buckets.Any(b => b.BucketName == bucketName);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "检查存储桶是否存在失败: {BucketName}", bucketName);
            throw;
        }
    }

    /// <summary>
    /// 创建存储桶
    /// </summary>
    public virtual async Task<bool> CreateBucketAsync(string bucketName, BucketAcl acl = BucketAcl.Private, CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

        try
        {
            // 检查存储桶是否已存在
            if (await BucketAnyAsync(bucketName, cancellationToken))
            {
                _logger.LogInformation("存储桶已存在: {BucketName}", bucketName);
                return true;
            }

            // 创建存储桶的请求
            var putBucketRequest = new PutBucketRequest
            {
                BucketName = bucketName,
                CannedACL = ConvertBucketAcl(acl)
            };

            // 执行创建操作
            await _s3Client.PutBucketAsync(putBucketRequest, cancellationToken);

            // 更新缓存
            _bucketCache.Set(bucketName, true);

            _logger.LogInformation("成功创建存储桶: {BucketName}, ACL: {Acl}", bucketName, acl);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "创建存储桶失败: {BucketName}", bucketName);
            return false;
        }
    }

    /// <summary>
    /// 删除存储桶
    /// </summary>
    public virtual async Task<bool> RemoveBucketAsync(string bucketName, CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

        try
        {
            // 检查存储桶是否存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                _logger.LogWarning("要删除的存储桶不存在: {BucketName}", bucketName);
                return false;
            }

            // 使用优化后的方法检查存储桶是否为空
            if (!await IsBucketEmptyAsync(bucketName, cancellationToken))
            {
                _logger.LogError("无法删除非空存储桶: {BucketName}", bucketName);
                return false;
            }

            // 删除存储桶
            await _s3Client.DeleteBucketAsync(bucketName, cancellationToken);

            // 更新缓存
            _bucketCache.Remove(bucketName);

            _logger.LogInformation("成功删除存储桶: {BucketName}", bucketName);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "删除存储桶失败: {BucketName}", bucketName);

            // 根据错误码判断处理方式
            HandleBucketOperationError(bucketName, ex);

            return false;
        }
    }

    /// <summary>
    /// 获取所有存储桶名称
    /// </summary>
    public virtual async Task<IEnumerable<string>> GetBucketNamesAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _s3Client.ListBucketsAsync(cancellationToken);
            var bucketNames = response.Buckets.Select(b => b.BucketName).ToList();

            // 更新缓存
            _bucketCache.UpdateAll(bucketNames);

            _logger.LogDebug("获取到 {Count} 个存储桶", bucketNames.Count);
            return bucketNames;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取存储桶列表失败");
            throw;
        }
    }

    #endregion 存储桶操作

    #region 存储对象操作

    /// <summary>
    /// 检查存储对象是否存在
    /// </summary>
    public virtual async Task<bool> ObjectsAnyAsync(string bucketName, string objectName, CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

        try
        {
            // 构建请求检查对象是否存在
            var request = new GetObjectMetadataRequest
            {
                BucketName = bucketName,
                Key = objectName
            };

            // 执行请求
            await _s3Client.GetObjectMetadataAsync(request, cancellationToken);
            return true;
        }
        catch (Exception ex)
        {
            var handledEx = HandleObjectOperationError(bucketName, objectName, ex);

            // 如果是对象不存在，返回false
            if (handledEx is FileNotFoundException)
            {
                return false;
            }

            // 其他异常则抛出
            if (handledEx != null)
            {
                throw handledEx;
            }

            return false;
        }
    }

    /// <summary>
    /// 获取存储对象
    /// </summary>
    public virtual async Task GetObjectAsync(
        string bucketName,
        string objectName,
        Func<Stream, Task> callback,
        Action<ProgressInfo>? progress = null,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");
        Check.NotNull(callback, nameof(callback), "回调函数不能为空");

        try
        {
            // 构建请求
            var request = new GetObjectRequest
            {
                BucketName = bucketName,
                Key = objectName
            };

            // 执行请求
            using var response = await _s3Client.GetObjectAsync(request, cancellationToken);

            var progressInfo = new ProgressInfo
            {
                TotalBytes = response.ContentLength,
                TransferredBytes = 0
            };

            // 使用自定义流包装器来跟踪进度
            using var progressStream = new ProgressStream(response.ResponseStream, progressInfo, progress);
            await callback(progressStream);
        }
        catch (Exception ex)
        {
            throw HandleObjectOperationError(bucketName, objectName, ex);
        }
    }

    /// <summary>
    /// 获取存储对象并保存到文件
    /// </summary>
    /// <summary>
    /// 获取存储对象并保存到文件
    /// </summary>
    public virtual async Task GetObjectAsync(
        string bucketName,
        string objectName,
        string filePath,
        Action<ProgressInfo>? progress = null,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");
        Check.StringNotNullOrWhiteSpace(filePath, nameof(filePath), "文件路径不能为空");

        try
        {
            // 确保目录存在
            var directory = Path.GetDirectoryName(filePath);
            if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            // 构建请求
            var request = new GetObjectRequest
            {
                BucketName = bucketName,
                Key = objectName
            };

            // 执行请求获取对象
            using var response = await _s3Client.GetObjectAsync(request, cancellationToken);

            // 创建进度信息对象
            var progressInfo = new ProgressInfo
            {
                TotalBytes = response.ContentLength,
                TransferredBytes = 0
            };

            // 创建文件流
            using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);

            // 使用缓冲区分块读取和写入
            byte[] buffer = new byte[81920]; // 80KB 缓冲区
            int bytesRead;
            long totalBytesRead = 0;

            while ((bytesRead = await response.ResponseStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken)) > 0)
            {
                await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);

                // 更新总读取字节数
                totalBytesRead += bytesRead;

                // 更新并报告进度
                if (progress != null)
                {
                    progressInfo.TransferredBytes = totalBytesRead;
                    progress(progressInfo);
                }
            }

            await fileStream.FlushAsync(cancellationToken);

            // 确保报告100%进度
            if (progress != null && totalBytesRead > 0)
            {
                progressInfo.TransferredBytes = progressInfo.TotalBytes;
                progress(progressInfo);
            }

            _logger.LogInformation(
                "成功下载对象到文件: Bucket={Bucket}, Object={Object}, File={FilePath}, Size={Size}",
                bucketName, objectName, filePath, totalBytesRead);
        }
        catch (Exception ex)
        {
            throw HandleObjectOperationError(bucketName, objectName, ex);
        }
    }

    /// <summary>
    /// 上传对象
    /// </summary>
    public virtual async Task<bool> PutObjectAsync(
        string bucketName,
        string objectName,
        Stream data,
        IDictionary<string, string>? metadata = null,
        ObjectAcl acl = ObjectAcl.Inherited,
        Action<ProgressInfo>? progress = null,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");
        Check.NotNull(data, nameof(data), "数据流不能为空");

        try
        {
            // 确保存储桶存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                if (!await CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                {
                    _logger.LogError("创建存储桶失败: {Bucket}", bucketName);
                    return false;
                }
            }

            // 准备上传请求
            var putRequest = new PutObjectRequest
            {
                BucketName = bucketName,
                Key = objectName,
                InputStream = data,
                AutoCloseStream = false,
                CannedACL = ConvertObjectAcl(acl)   //设置ACL权限
            };

            // 设置内容类型
            string? contentType = GetContentType(objectName);
            if (!string.IsNullOrEmpty(contentType))
            {
                putRequest.ContentType = contentType;
            }

            // 添加用户自定义元数据
            if (metadata != null && metadata.Count > 0)
            {
                foreach (var pair in metadata)
                {
                    putRequest.Metadata.Add("x-amz-meta-" + pair.Key, pair.Value);
                }
            }

            // 如果需要进度报告，使用事件处理
            if (progress != null)
            {
                var progressInfo = new ProgressInfo
                {
                    TotalBytes = data.Length,
                    TransferredBytes = 0
                };

                putRequest.StreamTransferProgress += (sender, args) =>
                {
                    progressInfo.TransferredBytes = args.TransferredBytes;
                    progress(progressInfo);
                };
            }

            // 执行上传
            await _s3Client.PutObjectAsync(putRequest, cancellationToken);
            _logger.LogInformation("成功上传对象: Bucket={Bucket}, Object={Object}, Size={Size}",
                bucketName, objectName, data.Length);

            return true;
        }
        catch (Exception ex)
        {
            HandleObjectOperationError(bucketName, objectName, ex);

            _logger.LogError(ex, "上传对象失败: Bucket={Bucket}, Object={Object}", bucketName, objectName);
            return false;
        }
    }

    /// <summary>
    /// 上传文件
    /// </summary>
    public virtual async Task<bool> PutObjectAsync(
        string bucketName,
        string objectName,
        string filePath,
        IDictionary<string, string>? metadata = null,
        ObjectAcl acl = ObjectAcl.Inherited,
        Action<ProgressInfo>? progress = null,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");
        Check.StringNotNullOrWhiteSpace(filePath, nameof(filePath), "文件路径不能为空");

        if (!File.Exists(filePath))
        {
            _logger.LogError("要上传的文件不存在: {FilePath}", filePath);
            return false;
        }

        try
        {
            // 确保存储桶存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                if (!await CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                {
                    _logger.LogError("创建存储桶失败: {Bucket}", bucketName);
                    return false;
                }
            }

            // 准备上传请求
            var fileInfo = new FileInfo(filePath);
            var putRequest = new PutObjectRequest
            {
                BucketName = bucketName,
                Key = objectName,
                FilePath = filePath,
                CannedACL = ConvertObjectAcl(acl) //设置ACL权限
            };

            // 设置内容类型
            string? contentType = GetContentType(objectName);
            if (string.IsNullOrEmpty(contentType))
            {
                contentType = GetContentType(filePath);
            }
            if (!string.IsNullOrEmpty(contentType))
            {
                putRequest.ContentType = contentType;
            }

            // 添加用户自定义元数据
            if (metadata != null && metadata.Count > 0)
            {
                foreach (var pair in metadata)
                {
                    putRequest.Metadata.Add("x-amz-meta-" + pair.Key, pair.Value);
                }
            }

            // 如果需要进度报告，使用事件处理
            if (progress != null)
            {
                var progressInfo = new ProgressInfo
                {
                    TotalBytes = fileInfo.Length,
                    TransferredBytes = 0
                };

                putRequest.StreamTransferProgress += (sender, args) =>
                {
                    progressInfo.TransferredBytes = args.TransferredBytes;
                    progress(progressInfo);
                };
            }

            // 执行上传
            await _s3Client.PutObjectAsync(putRequest, cancellationToken);
            _logger.LogInformation("成功上传文件: Bucket={Bucket}, Object={Object}, File={FilePath}, Size={Size}",
                bucketName, objectName, filePath, fileInfo.Length);

            return true;
        }
        catch (Exception ex)
        {
            HandleObjectOperationError(bucketName, objectName, ex);
            _logger.LogError(ex, "上传文件失败: Bucket={Bucket}, Object={Object}, File={FilePath}", bucketName, objectName, filePath);
            return false;
        }
    }

    /// <summary>
    /// 复制对象
    /// </summary>
    public virtual async Task<bool> CopyObjectAsync(
        string sourceBucketName,
        string sourceObjectName,
        string destBucketName,
        string? destObjectName = null,
        ObjectAcl acl = ObjectAcl.Inherited,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(sourceBucketName, nameof(sourceBucketName), "源存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(sourceObjectName, nameof(sourceObjectName), "源对象名称不能为空");
        Check.StringNotNullOrWhiteSpace(destBucketName, nameof(destBucketName), "目标存储桶名称不能为空");

        destObjectName ??= sourceObjectName;

        try
        {
            // 检查源对象是否存在
            if (!await ObjectsAnyAsync(sourceBucketName, sourceObjectName, cancellationToken))
            {
                _logger.LogError("源对象不存在: Bucket={Bucket}, Object={Object}",
                    sourceBucketName, sourceObjectName);
                return false;
            }

            // 确保目标存储桶存在
            if (!await BucketAnyAsync(destBucketName, cancellationToken))
            {
                if (!await CreateBucketAsync(destBucketName, BucketAcl.Private, cancellationToken))
                {
                    _logger.LogError("创建目标存储桶失败: {Bucket}", destBucketName);
                    return false;
                }
            }

            // 准备复制请求
            var copyRequest = new CopyObjectRequest
            {
                SourceBucket = sourceBucketName,
                SourceKey = sourceObjectName,
                DestinationBucket = destBucketName,
                DestinationKey = destObjectName,
                CannedACL = ConvertObjectAcl(acl)
            };

            // 执行复制
            var response = await _s3Client.CopyObjectAsync(copyRequest, cancellationToken);
            _logger.LogInformation("成功复制对象: Source={Source}, Destination={Destination}",
                $"{sourceBucketName}/{sourceObjectName}",
                $"{destBucketName}/{destObjectName}");

            return true;
        }
        catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound)
        {
            _logger.LogError("源对象不存在: Bucket={Bucket}, Object={Object}",
                sourceBucketName, sourceObjectName);
            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "复制对象失败: Source={Source}, Destination={Destination}",
                $"{sourceBucketName}/{sourceObjectName}",
                $"{destBucketName}/{destObjectName}");
            return false;
        }
    }

    /// <summary>
    /// 删除对象
    /// </summary>
    public virtual async Task<bool> RemoveObjectAsync(
        string bucketName,
        string objectName,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

        try
        {
            // 检查对象是否存在
            if (!await ObjectsAnyAsync(bucketName, objectName, cancellationToken))
            {
                _logger.LogWarning("要删除的对象不存在: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                return false;
            }

            // 准备删除请求
            var deleteRequest = new DeleteObjectRequest
            {
                BucketName = bucketName,
                Key = objectName
            };

            // 执行删除
            await _s3Client.DeleteObjectAsync(deleteRequest, cancellationToken);
            _logger.LogInformation("成功删除对象: Bucket={Bucket}, Object={Object}",
                bucketName, objectName);

            return true;
        }
        catch (Exception ex)
        {
            HandleObjectOperationError(bucketName, objectName, ex);

            _logger.LogError(ex, "删除对象失败: Bucket={Bucket}, Object={Object}",
                bucketName, objectName);

            return false;
        }
    }

    /// <summary>
    /// 批量删除对象
    /// </summary>
    public virtual async Task<bool> RemoveObjectsAsync(
        string bucketName,
        IEnumerable<string> objectNames,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.NotNull(objectNames, nameof(objectNames), "对象名称集合不能为空");

        var namesList = objectNames.ToList();
        if (namesList.Count == 0)
        {
            return true;
        }

        try
        {
            // 检查存储桶是否存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                _logger.LogError("存储桶不存在: {BucketName}", bucketName);
                return false;
            }

            // 准备批量删除请求
            var deleteRequest = new DeleteObjectsRequest
            {
                BucketName = bucketName,
                Objects = namesList.Select(name => new KeyVersion { Key = name }).ToList()
            };

            // S3 API限制一次最多删除1000个对象
            const int batchSize = 1000;
            bool allSucceeded = true;

            // 分批删除
            for (int i = 0; i < namesList.Count; i += batchSize)
            {
                var batchObjects = namesList
                    .Skip(i)
                    .Take(batchSize)
                    .Select(name => new KeyVersion { Key = name })
                    .ToList();

                deleteRequest.Objects = batchObjects;

                var response = await _s3Client.DeleteObjectsAsync(deleteRequest, cancellationToken);

                // 检查是否有删除失败的对象
                if (response.DeleteErrors.Count > 0)
                {
                    allSucceeded = false;
                    foreach (var error in response.DeleteErrors)
                    {
                        _logger.LogError(
                            "删除对象失败: Bucket={Bucket}, Object={Object}, Error={ErrorCode}, Message={Message}",
                            bucketName, error.Key, error.Code, error.Message);
                    }
                }

                _logger.LogInformation(
                    "批量删除对象批次完成: Bucket={Bucket}, BatchSize={BatchSize}, Success={Success}, Failed={Failed}",
                    bucketName, batchObjects.Count, batchObjects.Count - response.DeleteErrors.Count,
                    response.DeleteErrors.Count);
            }

            return allSucceeded;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "批量删除对象失败: Bucket={Bucket}, ObjectCount={Count}",
                bucketName, namesList.Count);
            return false;
        }
    }

    /// <summary>
    /// 获取对象元数据
    /// </summary>
    public virtual async Task<ObjectMetadata> GetObjectMetadataAsync(
        string bucketName,
        string objectName,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

        try
        {
            // 准备元数据请求
            var request = new GetObjectMetadataRequest
            {
                BucketName = bucketName,
                Key = objectName
            };

            // 获取元数据
            var response = await _s3Client.GetObjectMetadataAsync(request, cancellationToken);

            // 提取用户自定义元数据
            var userMetadata = new Dictionary<string, string>();
            foreach (var key in response.Metadata.Keys)
            {
                if (key.StartsWith("x-amz-meta-"))
                {
                    var userKey = key["x-amz-meta-".Length..];
                    userMetadata[userKey] = response.Metadata[key];
                }
                else
                {
                    userMetadata[key] = response.Metadata[key];
                }
            }

            // 创建ObjectMetadata对象
            var metadata = new ObjectMetadata
            {
                Size = response.ContentLength,
                LastModified = response.LastModified,
                ContentType = response.Headers.ContentType,
                ETag = response.ETag,
                UserMetadata = userMetadata
            };

            return metadata;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取对象元数据失败: Bucket={Bucket}, Object={Object}",
                bucketName, objectName);

            throw HandleObjectOperationError(bucketName, objectName, ex);
        }
    }

    /// <summary>
    /// 列举对象
    /// </summary>
    public virtual async Task<ListObjectsResult> ListObjectsAsync(
        string bucketName,
        string? prefix = null,
        string? continuationToken = null,
        int maxKeys = 1000,
        bool useHierarchy = true,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

        try
        {
            // 检查存储桶是否存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                _logger.LogError("存储桶不存在: {BucketName}", bucketName);
                throw new DirectoryNotFoundException($"存储桶不存在: {bucketName}");
            }

            // 准备列举请求
            var request = new ListObjectsV2Request
            {
                BucketName = bucketName,
                Prefix = prefix ?? string.Empty,
                ContinuationToken = continuationToken,
                MaxKeys = maxKeys,
                Delimiter = useHierarchy ? "/" : null       // 根据参数决定是否使用分隔符
            };

            // 执行列举
            var response = await _s3Client.ListObjectsV2Async(request, cancellationToken);

            // 构造返回结果
            var result = new ListObjectsResult
            {
                IsTruncated = response.IsTruncated,
                NextContinuationToken = response.NextContinuationToken,
                Objects = response.S3Objects.Select(obj => new ObjectInfo
                {
                    ObjectName = obj.Key,
                    Size = obj.Size,
                    LastModified = obj.LastModified,
                    ETag = obj.ETag
                }).ToList(),
                CommonPrefixes = response.CommonPrefixes.ToList()
            };

            return result;
        }
        catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound)
        {
            HandleBucketOperationError(bucketName, ex);

            _logger.LogError("存储桶不存在: {BucketName}", bucketName);
            throw new DirectoryNotFoundException($"存储桶不存在: {bucketName}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "列举对象失败: Bucket={Bucket}, Prefix={Prefix}",
                bucketName, prefix);
            throw;
        }
    }

    /// <summary>
    /// 获取对象访问URL
    /// </summary>
    public virtual async Task<string> GetPresignedGetUrlAsync(
        string bucketName,
        string objectName,
        TimeSpan expiry,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

        try
        {
            // 检查对象是否存在
            if (!await ObjectsAnyAsync(bucketName, objectName, cancellationToken))
            {
                _logger.LogError("要生成访问URL的对象不存在: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw new FileNotFoundException($"对象不存在: Bucket={bucketName}, Object={objectName}");
            }

            // 准备签名URL请求
            var request = new GetPreSignedUrlRequest
            {
                BucketName = bucketName,
                Key = objectName,
                Expires = DateTime.UtcNow.Add(expiry)
            };

            // 获取签名URL
            string url = _s3Client.GetPreSignedURL(request);

            // 如果配置了公共访问URL基础地址，则替换默认的S3服务URL
            if (!string.IsNullOrEmpty(_options.PublicBaseUrl))
            {
                // 提取请求路径和查询部分
                var uri = new Uri(url);
                var path = uri.AbsolutePath;
                var query = uri.Query;

                // 构建新的URL
                var publicBaseUrl = _options.PublicBaseUrl.TrimEnd('/');
                url = $"{publicBaseUrl}{path}{query}";
            }

            return url;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成预签名URL失败: Bucket={Bucket}, Object={Object}",
                bucketName, objectName);

            throw HandleObjectOperationError(bucketName, objectName, ex);
        }
    }

    /// <summary>
    /// 获取用于上传的预签名URL
    /// </summary>
    public virtual async Task<string> GetPresignedPutUrlAsync(
        string bucketName,
        string objectName,
        TimeSpan expiry,
        string? contentType = null,
        IDictionary<string, string>? metadata = null,
        CancellationToken cancellationToken = default)
    {
        Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
        Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

        try
        {
            // 确保存储桶存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                _logger.LogError("存储桶不存在: {BucketName}", bucketName);
                throw new DirectoryNotFoundException($"存储桶不存在: {bucketName}");
            }

            // 准备签名URL请求
            var request = new GetPreSignedUrlRequest
            {
                BucketName = bucketName,
                Key = objectName,
                Verb = HttpVerb.PUT,                    // 指定HTTP PUT方法
                Expires = DateTime.UtcNow.Add(expiry),
                Protocol = _options.UseHttps ? Protocol.HTTPS : Protocol.HTTP,
                ContentType = contentType ?? GetContentType(objectName)  // 设置内容类型
            };

            // 添加用户自定义元数据
            if (metadata != null && metadata.Count > 0)
            {
                foreach (var pair in metadata)
                {
                    // S3要求元数据键必须以x-amz-meta-开头
                    request.Metadata.Add($"{pair.Key.ToLowerInvariant()}", pair.Value);
                }
            }

            // 获取签名URL
            string url = _s3Client.GetPreSignedURL(request);

            // 如果配置了公共访问URL基础地址，则替换默认的S3服务URL
            if (!string.IsNullOrEmpty(_options.PublicBaseUrl))
            {
                // 提取请求路径和查询部分
                var uri = new Uri(url);
                var path = uri.AbsolutePath;
                var query = uri.Query;

                // 构建新的URL
                var publicBaseUrl = _options.PublicBaseUrl.TrimEnd('/');
                url = $"{publicBaseUrl}{path}{query}";
            }

            _logger.LogInformation(
                "成功生成上传用预签名URL: Bucket={Bucket}, Object={Object}, ContentType={ContentType}, Expiry={Expiry}",
                bucketName, objectName, contentType, expiry);

            return url;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成上传用预签名URL失败: Bucket={Bucket}, Object={Object}",
                bucketName, objectName);

            throw HandleObjectOperationError(bucketName, objectName, ex);
        }
    }

    #endregion 存储对象操作

    #region 辅助类

    /// <summary>
    /// 用于跟踪流传输进度的流包装器
    /// </summary>
    private class ProgressStream : Stream
    {
        private readonly Stream _innerStream;
        private readonly ProgressInfo _progressInfo;
        private readonly Action<ProgressInfo>? _progressCallback;
        private long _lastReportedBytes;
        private const int ProgressReportThreshold = 64 * 1024; // 每64KB报告一次进度

        public ProgressStream(Stream innerStream, ProgressInfo progressInfo, Action<ProgressInfo>? progressCallback)
        {
            _innerStream = innerStream;
            _progressInfo = progressInfo;
            _progressCallback = progressCallback;
            _lastReportedBytes = 0;
        }

        public override bool CanRead => _innerStream.CanRead;
        public override bool CanSeek => _innerStream.CanSeek;
        public override bool CanWrite => _innerStream.CanWrite;
        public override long Length => _innerStream.Length;
        public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; }

        public override void Flush() => _innerStream.Flush();

        public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);

        public override void SetLength(long value) => _innerStream.SetLength(value);

        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesRead = _innerStream.Read(buffer, offset, count);
            UpdateProgress(bytesRead);
            return bytesRead;
        }

        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            int bytesRead = await _innerStream.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
            UpdateProgress(bytesRead);
            return bytesRead;
        }

        public override int Read(Span<byte> buffer)
        {
            int bytesRead = _innerStream.Read(buffer);
            UpdateProgress(bytesRead);
            return bytesRead;
        }

        public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
        {
            int bytesRead = await _innerStream.ReadAsync(buffer, cancellationToken);
            UpdateProgress(bytesRead);
            return bytesRead;
        }

        private void UpdateProgress(int bytesRead)
        {
            if (_progressCallback == null || bytesRead <= 0)
                return;

            _progressInfo.TransferredBytes += bytesRead;
            long diff = _progressInfo.TransferredBytes - _lastReportedBytes;

            if (diff >= ProgressReportThreshold || _progressInfo.TransferredBytes >= _progressInfo.TotalBytes)
            {
                _progressCallback(_progressInfo);
                _lastReportedBytes = _progressInfo.TransferredBytes;
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _innerStream.Write(buffer, offset, count);
            UpdateProgress(count);
        }

        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            await _innerStream.WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
            UpdateProgress(count);
        }

        public override void Write(ReadOnlySpan<byte> buffer)
        {
            _innerStream.Write(buffer);
            UpdateProgress(buffer.Length);
        }

        public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            await _innerStream.WriteAsync(buffer, cancellationToken);
            UpdateProgress(buffer.Length);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _innerStream.Dispose();
            }
            base.Dispose(disposing);
        }
    }

    #endregion 辅助类

    #region IDisposable Support

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 释放托管资源
                _s3Client?.Dispose();
            }

            _disposed = true;
        }
    }

    ~S3OssService()
    {
        Dispose(false);
    }

    #endregion IDisposable Support
}